package cn._2dland.comment.easing
{
	import flash.geom.Point;

	/**
	 * 三阶贝塞尔曲线
	 */
	public class BezierCurve extends EasingBase
	{
		/** x(t)的各项系数 */
		private var ax:Number;
		private var bx:Number;
		private var cx:Number;

		/** y(t)的各项系数 */
		private var ay:Number;
		private var by:Number;
		private var cy:Number;

		/** 精度控制常数 */
		private static const EPSILON:Number = 0.001;

		/** 牛顿迭代次数 */
		private static const ITERATE_TIME:int = 8;

		/**
		 * 构造器
		 * @param p1 控制点1
		 * @param p2 控制点2
		 */		
		public function BezierCurve(p1:Point, p2:Point) {
			super(p1, p2);

			// 计算多项式系数
			cx = 3.0 * p1.x;
			bx = 3.0 * (p2.x - p1.x) - cx;
			ax = 1.0 - cx - bx;

			cy = 3.0 * p1.y;
			by = 3.0 * (p2.y - p1.y) - cy;
			ay = 1.0 - cy - by;
		}

		/**
		 * 获取指定时间点的属性值
		 * @param start 属性初始值
		 * @param end 属性最终值
		 * @param duration 变化时间
		 * @param time 时间点
		 * @return
		 */
		public function sample(start:Number, end:Number, duration:Number, time:Number):Number {
			var y:Number = solveY(time / duration);
			return y * (end - start) + start;
		}

		/**
		 * x(t)函数
		 * @param t
		 * @return 
		 */
		private function sampleCurveX(t:Number):Number {
			return ((ax * t + bx) * t + cx) * t;
		}

		/**
		 * y(t)函数
		 * @param t
		 * @return 
		 */
		private function sampleCurveY(t:Number):Number {
			return ((ay * t + by) * t + cy) * t;
		}

		/**
		 * x(t)的一阶导数
		 * @param t
		 * @return 
		 */
		private function sampleCurveXDerivative(t:Number):Number {
			return (3.0 * ax * t + 2.0 * bx) * t + cx;
		}

		/**
		 * 求x(t)=0的根
		 * @param x
		 * @return 
		 */
		private function solveCurveX(x:Number):Number {
			var t:Number = x;
			var f:Number;
			var fd:Number;

			// 使用牛顿迭代法求根
			for(var i=0; i<ITERATE_TIME; i++) {
				f = sampleCurveX(t) - x;
				if(Math.abs(f) < EPSILON) return t;

				fd = sampleCurveXDerivative(t);
				if(Math.abs(fd) < EPSILON) break;

				t = t - f / fd;
			}

			// 使用二分法求根
			var t0:Number = 0.0;
			if(t < t0) { return t0; }
			var t1:Number = 1.0;
			if(t > t1) { return t1; }
			while(t0 < t1) {
				f = sampleCurveX(t) - x;
				if(Math.abs(f) < EPSILON){
					break;
				}

				if(f < 0) {
					t0 = t;
				} else {
					t1 = t;
				}

				t = (t0 + t1) / 2 + t0;
			}

			return t;
		}

		/**
		 * 计算给定x对应的y值
		 * @param x
		 * @return 
		 */
		private function solveY(x:Number):Number {
			var t:Number = solveCurveX(x);
			return sampleCurveY(t);
		}
	}
}